<?PHP
/*

OpenTrack API PHP Framework

This software is written to expedite clean and correct API calls to the DealerTrack Webservices
collectively known as "OpenTrack".  This framework is a simple introduction to how the API
works.  There are areas of incompleteness and oppourtunities for improvement.  If there are
features you need assistance with please contact jarrett@crownmotors.pro

Open Source Initiative OSI - The MIT License:Licensing

The MIT License

We have no copyright on this code. Infact this code is not worthy
of a copyright. However if it helps you in anyway
please send thanks to Jarrett Kuljis @ jarrett@crownmotors.pro

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

*/

class ArkonaDMS_Interop
{
	
	/* Required Variables */
	
	const username	=	'';	// OpenTrack Issued Username
	const password	=	''; // OpenTrack Issued Password	
	const service	=	'https://fqdn/opentrack/webservice.asmx'; // Full URL to webservice
	
	const ecode		=	'';	// Enterprise Code (4 Digits)
	const dcode		=	''; // Dealer Code or General Ledger Code (3 Digits)
	const sname		=	'arkona(#).arkona.com'; // DNS FQDN server name, IE arkona3.arkona.com 
	const agent		=	'OpenTrackIntegrationAgent';	//	Agent string to pass in the HTTPS request. (not required)
	
	const libdir	=	'/home/opentrack/opentrack/';	// Required to be changed to the location you are placing the classes on your dir
	const err_log	=	"/tmp/opentracklogs/opentrack-request-error.log"; 
	const acc_log	=	"/tmp/opentracklogs/opentrack-request-access.log";
	
	/* End of required variables */
		
	public static $results			=	'';
	public static $transxml			=	'';
	protected static $method		=	'';
	public static $returnXML		=	true;
	protected static $testXML		=	false;		
	
	public function __construct() {
		ArkonaDMS_Interop::initCheck();				
		include(ArkonaDMS_Interop::libdir."classes/OT_Customer.php");
		include(ArkonaDMS_Interop::libdir."classes/OT_Service.php");
		include(ArkonaDMS_Interop::libdir."classes/OT_Vehicle.php");
		include(ArkonaDMS_Interop::libdir."classes/OT_Parts.php");
		include(ArkonaDMS_Interop::libdir."classes/OT_GL.php");
		include(ArkonaDMS_Interop::libdir."classes/OT_Deal.php");
	}
	
	public function initCheck(){
		// Check Log dir for write access
		if(!file_exists(ArkonaDMS_Interop::err_log)){
			touch(ArkonaDMS_Interop::err_log) or die("You must create error log file, it does not exist. (Folder is not writable)");
			if(!is_writable(ArkonaDMS_Interop::err_log)) {
				ArkonaDMS_Interop::eLog("Error Log not writable, please make ".$err_log." writable by the webserver daemon");
			}
		}
		if(!file_exists(ArkonaDMS_Interop::acc_log)) {
			touch(ArkonaDMS_Interop::acc_log) or die("You must create the access log file it does not exist. (Folder is not writable)");
			if(!is_writable(ArkonaDMS_Interop::acc_log)) {
				ArkonaDMS_Interop::eLog("Error Log not writable, please make ".$acc_log." writable by the webserver daemon");
			}
		}
		if(
			ArkonaDMS_Interop::username == '' or
			ArkonaDMS_Interop::password == '' or
			ArkonaDMS_Interop::service == '' or
			ArkonaDMS_Interop::ecode == '' or
			ArkonaDMS_Interop::dcode == '' or
			ArkonaDMS_Interop::sname == '' or
			ArkonaDMS_Interop::libdir == ''
		) 
		{	
			echo("REQUIRED VARIABLES NOT SET");
			@ArkonaDMS_Interop::eLog("Fatal Error: REQUIRED VARIABLES NOT SET");
			exit();	
		}
	}
	
	public function execRequest($soap) {
	   
		  if($soap == '') { 
			ArkonaDMS_Interop::eLog("No SOAP Package to transmit, application exiting...");
			return false;
		  }	
		
		  if(!function_exists('curl_init')) {
			ArkonaDMS_Interop::eLog("cURL not installed on server, Integration can not function");
			return false;
		  }			
		  
		  $header_array[] = "SOAPAction: http://www.starstandards.org/webservices/2005/10/transport/operations/ProcessMessage";
		  $header_array[] = "Content-type: text/xml";

		  $ch = curl_init();
		  curl_setopt($ch, CURLPROTO_HTTPS, true);
		  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
		  curl_setopt($ch, CURLOPT_HTTPHEADER, $header_array);
		  curl_setopt($ch, CURLOPT_POST, true);
		  curl_setopt($ch, CURLOPT_URL, ArkonaDMS_Interop::service);
		  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
		  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		  curl_setopt($ch, CURLOPT_USERAGENT, ArkonaDMS_Interop::agent);
		  curl_setopt($ch, CURLOPT_POSTFIELDS, ArkonaDMS_Interop::buildRequest($soap));
		  $datum	=	curl_exec($ch);
		  
		  if($datum == false) {
		  	
		  	ArkonaDMS_Interop::eLog("Transport Layer Failure #1 trying again...");
			// Never sure why but seem to not get response at times. If that is the case, try again.
			
			  curl_close($ch);
			  $ch = curl_init();
			  curl_setopt($ch, CURLPROTO_HTTPS, true);
			  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
			  curl_setopt($ch, CURLOPT_HTTPHEADER, $header_array);
			  curl_setopt($ch, CURLOPT_POST, true);
			  curl_setopt($ch, CURLOPT_URL, ArkonaDMS_Interop::service);
			  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
			  curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
			  curl_setopt($ch, CURLOPT_USERAGENT, ArkonaDMS_Interop::agent);
			  curl_setopt($ch, CURLOPT_POSTFIELDS, ArkonaDMS_Interop::buildRequest($soap));
			  $datum	=	curl_exec($ch);			
			
				if($datum == false) {
					ArkonaDMS_Interop::eLog("Transport Layer Failure ".ArkonaDMS_Interop::buildRequest($soap)." ");	
					return false;
				}
		  }
		  
		  curl_close($ch);
		  
		  // Return raw XML 
		  if(self::$returnXML == true) {
		  	self::$transxml .= ArkonaDMS_Interop::buildRequest($soap)." ".$datum;
		  }
  		  
		  
		  if(self::$testXML == true) {
		  		file_put_contents(ArkonaDMS_Interop::logdir.time().".xml", ArkonaDMS_Interop::buildRequest($soap));
		  }
		  
		  $object	=	simplexml_load_string(ArkonaDMS_Interop::openEnvelope($datum));
		  
		  // Error Validation
		  ArkonaDMS_Interop::errorValidation($object);
		  
		return $object;
	}	
	
	public function buildRequest($soap_request) {
		// Function add's SOAP Package to request...
			if($soap_request == '') {
				ArkonaDMS_Interop::eLog("Empty SOAP request, unable to build envelope, exiting...");
				return false;
			}
		
					$dtrack_soap_request = 
					'<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:tran="http://www.starstandards.org/webservices/2005/10/transport">
						<soap:Header>
							<wsse:Security soap:mustUnderstand="1" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
								<wsse:UsernameToken>
									<wsse:Username>'.ArkonaDMS_Interop::username.'</wsse:Username> 
									<wsse:Password>'.ArkonaDMS_Interop::password.'</wsse:Password> 
									<wsu:Created>2010-01-25T13:33:51Z</wsu:Created> 
								</wsse:UsernameToken> 
							</wsse:Security> 
							<tran:PayloadManifest>
								<tran:manifest contentID="Content0" namespaceURI="CrownDMSInterop" element="1" relatedID="1" version="1.0"/> 
							</tran:PayloadManifest> 
						</soap:Header> 
						<soap:Body>
							<tran:ProcessMessage>
								<tran:payload>
									<tran:content id="Content0">
									';
						$dtrack_soap_request .= $soap_request;
						$dtrack_soap_request .= '                    
									</tran:content> 
						   </tran:payload> 
					</tran:ProcessMessage> 
				</soap:Body> 
			</soap:Envelope>';
			
		return ArkonaDMS_Interop::cleanupXML($dtrack_soap_request);
	
	}

	public function addDealerCode() {
		$ret_string	= '
				<Dealer>
					<EnterpriseCode>'.ArkonaDMS_Interop::ecode.'</EnterpriseCode> 
					<CompanyNumber>'.ArkonaDMS_Interop::dcode.'</CompanyNumber> 
					<ServerName>'.ArkonaDMS_Interop::sname.'</ServerName> 
				</Dealer>
				';
		return $ret_string;
	}	
	
	public function openEnvelope($xml) {
		$str_pos1	=	strpos($xml, '<payload>');
		$str_pos2	=	strpos($xml, '</payload>');
		$content	=	substr($xml, $str_pos1, (($str_pos2 + strlen('</payload>')) - $str_pos1));
		return $content;
	}
	
	public function errorValidation($obj) {
		
			// Decalre non-fatal errors
			$non_fatal_errors		=	array(	"CS02"=>"No customers matching search criteria.",
												"VL02"=>"Vehicle not found.",
												"PPCT01"=>"No records found.", //Parts
												"POH01"=>"Parts HIstory Call No records found.",
												"PSH01"=>"Parts Sales History No records found.",
												"CROL02"=>"No closed RO's found",
												"GDL01"=>"A valid parameter combination must be provided."
											 );  	
			
			  foreach($obj->children() as $gen2) {
				 foreach($gen2->children() as $gen3) {
					// Check for Error String, if exists log and exit...
					if(strval($gen3->{'Error'}->{'Code'}) != '') {
						$message_to_user = "[".strval($gen3->{'Error'}->{'Code'})."] ".strval($gen3->{'Error'}->{'Message'})."";
						
						// Check Error to see if it is fatal
						if(!array_key_exists(strval($gen3->{'Error'}->{'Code'}), $non_fatal_errors)) {
							ArkonaDMS_Interop::eLog($message_to_user);
							// exit();
						}
					}
					
					// Check for Success String, Log and proceede.
					if(strval($gen3->{'Success'}->{'Message'}) != '') {
						$message_to_user = "[".strval($gen3->{'Success'}->{'Message'})."]";
						ArkonaDMS_Interop::aLog($message_to_user);
					}
				 }
			  }
			  
		  return false;
	}
	
	public function eLog($msg) {
		// echo $msg;
		error_log(date('c').":".$msg."\n", 3, ArkonaDMS_Interop::err_log);
	}
	
	public function aLog($msg) {
		error_log(date('c').":".$msg."\n", 3, ArkonaDMS_Interop::acc_log);
	}
	
	public function cleanupXML($xml) {
		$xmlOut = '';
		$inTag = false;
		$xmlLen = strlen($xml);
		for($i=0; $i < $xmlLen; ++$i) {
			$char = $xml[$i];
			// $nextChar = $xml[$i+1];
			switch ($char) {
			case '<':
			  if (!$inTag) {
				  // Seek forward for the next tag boundry
				  for($j = $i+1; $j < $xmlLen; ++$j) {
					 $nextChar = $xml[$j];
					 switch($nextChar) {
					 case '<':  // Means a < in text
					   $char = htmlentities($char);
					   break 2;
					 case '>':  // Means we are in a tag
					   $inTag = true;
					   break 2;
					 }
				  }
			  } else {
				 $char = htmlentities($char);
			  }
			  break;
			case '>':
			  if (!$inTag) {  // No need to seek ahead here
				 $char = htmlentities($char);
			  } else {
				 $inTag = false;
			  }
			  break;
			default:
			  if (!$inTag) {
				 $char = htmlentities($char);
			  }
			  break;
			}
			$xmlOut .= $char;
		}
		return $xmlOut;
	  }

}

?>
